/*
 * storage.c
 *
 *  Created on: Oct 17, 2013
 *      Author: Ken Arok
 */

#include "compiler.h"
#include "preprocessor.h"
#include "FreeRTOS.h"
#include "semphr.h"

#include "sdmmc_stack.h"
#include "storage.h"

/** \brief Lock Access to LUNs.
 *
 * \return true if access is successfully locked. Unless false.
 */
#define StorageAccessLock()		storage_access_lock()


/** \brief Unlock Access to LUNs.
 *
 */
#define StorageAccessUnlock()	xSemaphoreGive(pvSemaphoreStorage)


static xSemaphoreHandle pvSemaphoreStorage = NULL;


#if MAX_LUN

/** \brief Initialize an entry of the LUN descriptor table.
 *
 */
#if ACCESS_USB == true && ACCESS_MEM_TO_RAM == true
#define LUN_Desc_Entry(LUN) \
{	\
	TPASTE3(LUN_, LUN, _TestUnitReady),	\
	TPASTE3(LUN_, LUN, _ReadCapacity),	\
	TPASTE3(LUN_, LUN, _IsWriteProtected),	\
	TPASTE3(LUN_, LUN, _IsRemoval),	\
	TPASTE3(LUN_, LUN, _USBRead),	\
	TPASTE3(LUN_, LUN, _USBWrite),	\
	TPASTE3(LUN_, LUN, _MemoryToRAM),	\
	TPASTE3(LUN_, LUN, _RAMToMemory),	\
	TPASTE3(LUN_, LUN, _NAME) \
}
#elif ACCESS_USB == true
#define LUN_Desc_Entry(LUN) \
{	\
	TPASTE3(LUN_, LUN, _TestUnitReady),	\
	TPASTE3(LUN_, LUN, _ReadCapacity),	\
	TPASTE3(LUN_, LUN, _IsWriteProtected),	\
	TPASTE3(LUN_, LUN, _IsRemoval),	\
	TPASTE3(LUN_, LUN, _USBRead),	\
	TPASTE3(LUN_, LUN, _USBWrite),	\
	TPASTE3(LUN_, LUN, _NAME) \
}
#elif ACCESS_MEM_TO_RAM == true
#define LUN_Desc_Entry(LUN) \
{	\
	TPASTE3(LUN_, LUN, _TestUnitReady),	\
	TPASTE3(LUN_, LUN, _ReadCapacity),	\
	TPASTE3(LUN_, LUN, _IsWriteProtected),	\
	TPASTE3(LUN_, LUN, _IsRemoval),	\
	TPASTE3(LUN_, LUN, _MemoryToRAM),	\
	TPASTE3(LUN_, LUN, _RAMToMemory),	\
	TPASTE3(LUN_, LUN, _NAME) \
}
#else
#define LUN_Desc_Entry(LUN) \
{	\
	TPASTE3(LUN_, LUN, _TestUnitReady),	\
	TPASTE3(LUN_, LUN, _ReadCapacity),	\
	TPASTE3(LUN_, LUN, _IsWriteProtected),	\
	TPASTE3(LUN_, LUN, _IsRemoval),	\
	TPASTE3(LUN_, LUN, _NAME) \
}

#endif /* ACCESS_USB == true && ACCESS_MEM_TO_RAM == true */


/* LUN descriptor table. */
static const struct {
	storage_status_t (*TestUnitReady)(void);
	storage_status_t (*ReadCapacity)(uint32_t *);
	bool (*IsWriteProtected)(void);
	bool (*IsRemoval)(void);

#if ACCESS_USB == true
	storage_status_t (*USBRead)(uint32_t, uint16_t);
	storage_status_t (*USBWrite)(uint32_t, uint16_t);
#endif

#if ACCESS_MEM_TO_RAM == true
	storage_status_t (*MemoryToRAM)(uint32_t, void *);
	storage_status_t (*RAMToMemory)(uint32_t, const void *);
#endif

	const char *name;
} lun_desc[MAX_LUN] =
{
#if LUN_0 == ENABLE
	LUN_Desc_Entry(0),
#endif

#if LUN_1 == ENABLE
	LUN_Desc_Entry(1),
#endif

#if LUN_2 == ENABLE
	LUN_Desc_Entry(2),
#endif

#if LUN_3 == ENABLE
	LUN_Desc_Entry(3),
#endif

#if LUN_4 == ENABLE
	LUN_Desc_Entry(4),
#endif

#if LUN_5 == ENABLE
	LUN_Desc_Entry(5),
#endif

#if LUN_6 == ENABLE
	LUN_Desc_Entry(6),
#endif

#if LUN_7 == ENABLE
	LUN_Desc_Entry(7)
#endif
};



/** \brief Lock Access to LUNs.
 *
 * \return true if access is successfully locked. Unless false.
 */
static bool storage_access_lock(void);

/** \brief Initialize for Access Storage.
 *
 */
static bool storage_access_init(void);

/* ----------------------------------------------------------------------------------- */
storage_status_t vStorage_TestUnitReady(uint8_t lun)
{
	storage_status_t _status;

	if(!StorageAccessLock()) return STORAGE_FAIL;

	_status =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].TestUnitReady() :
		#if LUN_USB == ENABLE
			LUN_USB_TestUnitReady(lun - LUN_ID_USB);
		#else
			STORAGE_FAIL;
		#endif
	#endif

	StorageAccessUnlock();

	return _status;
}


storage_status_t vStorage_ReadCapacity(uint8_t lun, uint32_t *nb_sector)
{
	storage_status_t _status;

	if(!StorageAccessLock()) return STORAGE_FAIL;

	_status =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].ReadCapacity(nb_sector) :
		#if LUN_USB == ENABLE
			LUN_USB_ReadCapacity(lun - LUN_ID_USB, nb_sector);
		#else
			STORAGE_FAIL;
		#endif
	#endif

	StorageAccessUnlock();

	return _status;
}


uint8_t vStorage_SectorSize(uint8_t lun)
{
	uint8_t _sector_size;

	if(!StorageAccessLock()) return 0;

	_sector_size =
	#if MAX_LUN
		(lun < MAX_LUN) ? 1 :
		#if LUN_USB == ENABLE
			LUN_USB_ReadSectorSize(lun - LUN_ID_USB);
		#else
			0;
		#endif
	#endif

	StorageAccessUnlock();

	return _sector_size;
}


bool vStorage_IsWriteProtected(uint8_t lun)
{
	bool _wr_protect;

	if(!StorageAccessLock()) return true;

	_wr_protect =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].IsWriteProtected() :
		#if LUN_USB == ENABLE
				LUN_USB_IsWriteProtected(lun - LUN_ID_USB);
		#else
			true;
		#endif
	#endif

	StorageAccessUnlock();

	return _wr_protect;
}


bool vStorage_IsRemoval(uint8_t lun)
{
	bool _removal;

	if(!StorageAccessLock()) return true;

	_removal =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].IsRemoval() :
		#if LUN_USB == ENABLE
				LUN_USB_IsRemoval(lun - LUN_ID_USB);
		#else
			true;
		#endif
	#endif

	StorageAccessUnlock();

	return _removal;
}


const char *vStorage_Name(uint8_t lun)
{
#if MAX_LUN == 0
	UNUSED(lun);
#endif

	return

	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].name :
	#endif

	#if LUN_USB == ENABLE
		LUN_USB_NAME;
	#else
		NULL;
	#endif
}


#if ACCESS_USB == true

storage_status_t vStorage_MemoryToUSB(uint8_t lun, uint32_t addr, uint16_t nb_sector)
{
	storage_status_t _status;

	if(!StorageAccessLock()) return STORAGE_FAIL;

	vStorage_MemoryStartReadAction(nb_sector);

	_status =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].USBRead(addr, nb_sector) :
	#endif
		STORAGE_FAIL;

	vStorage_MemoryStopReadAction();

	StorageAccessUnlock();

	return _status;
}


storage_status_t vStorage_USBToMemory(uint8_t lun, uint32_t addr, uint16_t nb_sector)
{
	storage_status_t _status;

	if(!StorageAccessLock()) return STORAGE_FAIL;

	vStorage_MemoryStartWriteAction(nb_sector);

	_status =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].USBWrite(addr, nb_sector) :
	#endif
		STORAGE_FAIL;

	vStorage_MemoryStopWriteAction();

	StorageAccessUnlock();

	return _status;
}

#endif /* ACCESS_USB */


#if ACCESS_MEM_TO_RAM == true

storage_status_t vStorage_MemoryToRAM(uint8_t lun, uint32_t addr, void *ram)
{
	storage_status_t _status;

#if MAX_LUN == 0
	UNUSED(lun);
#endif

	if(!StorageAccessLock()) return STORAGE_FAIL;

	vStorage_MemoryStartReadAction(1);

	_status =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].MemoryToRAM(addr, ram) :
	#endif

	#if LUN_USB == ENABLE
		LUN_USB_MemoryToRAM(addr, ram)
	#else
		STORAGE_FAIL;
	#endif

	vStorage_MemoryStopReadAction();

	StorageAccessUnlock();

	return _status;
}


storage_status_t vStorage_RAMToMemory(uint8_t lun, uint32_t addr, const void *ram)
{
	storage_status_t _status;

#if MAX_LUN == 0
	UNUSED(lun);
#endif

	if(!StorageAccessLock()) return STORAGE_FAIL;

	vStorage_MemoryStartWriteAction(1);

	_status =
	#if MAX_LUN
		(lun < MAX_LUN) ? lun_desc[lun].RAMToMemory(addr, ram) :
	#endif

	#if LUN_USB == ENABLE
		LUN_USB_RAMToMemory(addr, ram)
	#else
		STORAGE_FAIL;
	#endif

	vStorage_MemoryStopWriteAction();

	StorageAccessUnlock();

	return _status;
}

#endif /* ACCESS_MEM_TO_RAM */

/* ------------------------------------------------------------------------------------ */
static bool storage_access_lock(void)
{
	/* If semaphore could not be created, there is no backup solution. */
	storage_access_init();

	/* Wait for the semaphore. */
	while(pdTRUE != xSemaphoreTake(pvSemaphoreStorage, 100));

	return true;
}


static bool storage_access_init(void)
{
	if(pvSemaphoreStorage == NULL) {
		/* Create semaphore for storage access. */
		vSemaphoreCreateBinary(pvSemaphoreStorage);
		if(pvSemaphoreStorage == NULL) {
			return false;
		}
	}

	return true;
}


#endif /* MAX_LUN */



